#include "ffhttpd.h"

/**
 * 将输入的字符串参数 s 转换为全部小写
 */
char *my_strlwr(char *s)
{
	char *p = s; // 创建一个指向字符串s的指针p
	while (*p)	 // 循环遍历字符串s中的每个字符
	{
		*p += *p > 'A' && *p < 'Z' ? 'a' - 'A' : 0; // 如果字符是大写字母,则将其转换为小写
		p++;										// 移动指针到下一个字符
	}
	return s;
}

/**
 * get_file_range_size函数用于获取指定文件的范围大小
 */
void get_file_range_size(char *file, int *start, int *end, int *size)
{
	FILE *fp = NULL;										  // 声明一个FILE类型的指针变量fp,用于打开文件
	char path[1024];										  // 声明一个字符数组变量path,用于存储文件的完整路径
	snprintf(path, sizeof(path), "%s/%s", g_root_path, file); // 使用snprintf函数将文件路径构造出来,
	fp = fopen(path, "rb");									  // 尝试使用fp指针打开文件,以二进制模式("rb")读取文件内容
	if (fp)													  // 如果文件打开成功
	{
		fseek(fp, 0, SEEK_END);		 // 将文件指针移动到文件末尾
		*size = ftell(fp);			 // 读取当前文件指针的位置,即文件的大小,并保存到size指向的地址中
		fclose(fp);					 // 关闭文件fp,释放资源
		if (*start < 0)				 // 如果start小于0
			*start = *size + *start; // 将其设置为size加上start的值,实现取反的效果
		if (*end >= *size)			 // 如果end大于等于size
			*end = *size - 1;		 // 将其设置为size减1,实现范围调整的效果
	}
	else
	{
		// 如果文件打开失败,则将start、end和size都设置为0、-1,表示文件不存在或无法读取
		*start = *end = 0;
		*size = -1;
	}
}

/**
 * send_file_data函数用于将指定文件的部分内容通过套接字发送到网络上
 */
void send_file_data(int fd, char *file, int start, int end)
{
	FILE *fp = NULL;										  // 声明一个FILE类型的指针变量fp,用于打开文件
	char path[1024];										  // 声明一个字符数组变量path,用于存储文件的完整路径
	snprintf(path, sizeof(path), "%s/%s", g_root_path, file); // 使用snprintf函数将文件路径构造出来
	fp = fopen(path, "rb");									  // 尝试使用fp指针打开文件,以二进制模式("rb")读取文件内容
	if (fp)													  // 如果文件打开成功
	{
		char buf[1024];						   // 声明一个字符数组变量buf,用于存储从文件中读取的数据
		int len = end - start + 1, ret = 0, n; // 声明一个整型变量len,用于计算要读取的字节数
		fseek(fp, start, SEEK_SET);			   // 将文件指针定位到要读取的起始位置,偏移量为start
		do									   // 进入一个循环,条件为len大于0且没有到达文件末尾
		{
			n = len < sizeof(buf) ? len : sizeof(buf); // 计算要读取的字节数,如果小于buf的大小,就等于len,否则等于buf的大小
			n = (int)fread(buf, 1, n, fp);			   // 从文件中读取n个字节的数据到buf中,返回读取的字节数
			len -= n > 0 ? n : 0;					   // 将len减去已读取的字节数,如果读取成功,n会大于0,否则为0
			while (n > 0)							   // 进入一个循环,条件为n大于0,即还有数据要发送
			{
				ret = send(fd, buf, n, 0);											 // 尝试通过套接字fd发送buf中的n个字节的数据,不进行阻塞处理
				if (ret == 0 || (ret < 0 && errno != EWOULDBLOCK && errno != EINTR)) // 如果发送失败,或者发送的字节数为0(被阻塞或中断)
					goto done;														 // 跳出内部循环,执行下面的代码块
				n -= ret > 0 ? ret : 0;												 // 将n减去已经成功发送的字节数,如果ret大于0(发送成功),则n会减去ret,否则保持为0
			}
		} while (len > 0 && !feof(fp));
	done:
		fclose(fp); // 关闭文件fp,释放资源
	}
}

/**
 * 从输入的字符串str中查找指定的关键字key,并提取出该关键字后的对应值存储到val中.
 * 如果找不到关键字或者没有对应的值,函数将返回NULL.
 */
char *parse_params(char *str, const char *key, char *val, int len)
{
	char *p = (char *)strstr(str, key); // 定义一个字符指针p,并将其初始化为str中key的位置
	int i;								// 循环变量

	*val = '\0'; // 将 val 指向的地址的值设为 null 字符,即清空 val 指向的内存区域
	if (!p)		 // 如果 p 为 NULL,即没有找到 key,则返回 NULL
	{
		return NULL;
	}
	p += strlen(key); // 将 p 移动到 key 在 str 中的下一个字符的位置
	if (*p == '\0')	  // 如果 p 指向的字符是 null 字符,即没有对应的值,则返回 NULL
	{
		return NULL;
	}

	// 循环遍历 p 指向的字符,直到遇到 ':' 或者 ' ' 字符为止
	while (*p)
	{
		// 如果当前字符不是 ':' 也不是 ' ',则跳出循环
		if (*p != ':' && *p != ' ')
			break;
		else
			p++;
	}

	// 通过 for 循环,将 p 指向的字符(不包括特殊字符 ':' 和 ' ')复制到 val 中
	// 这里复制到 val 中的字符不包括后面的 '\0' 字符,因此复制结束后需要手动添加 '\0' 到 val 的末尾
	for (i = 0; i < len; i++)
	{
		if (*p == '\\')					   // 如果当前字符是 '\'
			p++;						   // 跳过下一个字符
		else if (*p == '\r' || *p == '\n') // 如果当前字符是 '\r'(回车)或者 '\n'(换行)
			break;						   // 跳出循环
		val[i] = *p++;					   // 否则将当前字符复制到 val 中
	}
	// 在 val 的末尾添加 '\0' 字符,表示字符串的结束
	val[i] = val[len - 1] = '\0';
	return val;
}